/*
 * Copyright (c) 2010-2011 Josef Hardi <josef.hardi@gmail.com>
 * Copyright (c) 2010 Piotr Zagawa <piotr.zagawa@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package id.duapuluhdua.dakidaki.compass;

import java.util.Observable;
import java.util.Observer;

import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.location.Location;

import id.duapuluhdua.dakidaki.Command;
import id.duapuluhdua.dakidaki.MainState;
import id.duapuluhdua.dakidaki.activities.ActivityMain;
import id.duapuluhdua.dakidaki.common.CommonActivity;
import id.duapuluhdua.dakidaki.map.GeoPoint;
import id.duapuluhdua.dakidaki.map.Mercator;
import id.duapuluhdua.dakidaki.map.Screen;
import id.duapuluhdua.dakidaki.map.ScreenPoint;

/**
 *
 */
public class CompassRotation implements Observer
{
  /** Fields */
  private static CompassRotation self = null;

  private MainState mState;
  private Compass mCompass;

  // For the rotation properties
  private float mAzimuth;
  private float mCenterX;
  private float mCenterY;

  private GeoPoint mGeoPoint;
  private ScreenPoint mScreenPoint;
  private Matrix mMatrix;

  private boolean bIsLocation;
  private boolean bEnabled;
  private boolean bCanvasSaved;

  private float[] mVecPoint = { 0, 0 };

  /**
   * The constructor.
   *
   * @param parent the main user interaction handler.
   */
  public CompassRotation(CommonActivity parent)
  {
    self = this;

    mAzimuth = 0;
    mCenterX = 0;
    mCenterY = 0;

    bEnabled = false;
    bCanvasSaved = false;
    bIsLocation = false;

    mCompass = new Compass(parent);

    mGeoPoint = new GeoPoint();
    mScreenPoint = new ScreenPoint();
    mMatrix = new Matrix();

    mState = parent.getMainState();
    mState.addObserver(this); // watch state object for UI mode change
  }

  public void enable()
  {
    mCompass.start();
    bEnabled = true;
  }

  public void disable()
  {
    mCompass.stop();
    bEnabled = false;
    mAzimuth = 0;
  }

  public void updateState(Screen screen, Canvas canvas, Rect rtBounds)
  {
    bIsLocation = false;
    mMatrix = canvas.getMatrix();

    if (mState.getCurrentUiMode() == Command.CMD_MODE_MAP) {
      // Get the last location
      final Location location = ActivityMain.getLastLocation();

      if (location != null) {
        bIsLocation = true;

        // Update declination at start for location approximation.
        mCompass.updateTrueNorthCorrection(location);

        // Get azimuth
        mAzimuth = mCompass.getAzimuth();

        mGeoPoint.wgsLon = location.getLongitude();
        mGeoPoint.wgsLat = location.getLatitude();

        Mercator.convertToFlat(mGeoPoint);

        screen.toScreenPoint(mGeoPoint, mScreenPoint);

        mCenterX = mScreenPoint.x;
        mCenterY = mScreenPoint.y;

        // Apply compass rotation with negate azimuth for screen view.
        mMatrix.postRotate(-mAzimuth, mCenterX, mCenterY);
      }
      else {
        mCenterX = rtBounds.centerX();
        mCenterY = rtBounds.centerY();
      }
    }
  }

  public void set(Canvas canvas)
  {
    if (bEnabled && bIsLocation) {
      canvas.save();
      bCanvasSaved = true;

      // Apply compass rotation with negate azimuth for screen view.
      canvas.rotate(-mAzimuth, mCenterX, mCenterY);
    }
  }

  public void clear(Canvas canvas)
  {
    if (bCanvasSaved) {
      bCanvasSaved = false;
      canvas.restore();
    }
  }

  public void transformPoint(ScreenPoint point)
  {
    if (bEnabled && bIsLocation) {
      mVecPoint[0] = point.x;
      mVecPoint[1] = point.y;

      mMatrix.mapPoints(mVecPoint);

      point.x = (int) mVecPoint[0];
      point.y = (int) mVecPoint[1];
    }
  }

  @Override
  public void update(Observable observable, Object data)
  {
    // Watch for UI state change
    if (observable == mState) {
      if (data == MainState.UI_MODE_UPDATE) {
        disable();
        if (mState.getCurrentUiMode() == Command.CMD_MODE_MAP) {
          enable();
        }
      }
    }
  }

  public static int rotationCenterX()
  {
    return (int) self.mCenterX;
  }

  public static int rotationCenterY()
  {
    return (int) self.mCenterY;
  }

  public static float getAzimuth()
  {
    return self.mCompass.getAzimuth();
  }
}
